package mango.orm.entity;

import java.io.InputStream;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import mango.functions.FuncDate;
import mango.functions.FuncString;
import mango.orm.DBSession;
import mango.orm.DBSessionFactory;
import mango.orm.connect.Connector;



@SuppressWarnings({ "unchecked", "serial" })
public class Entity implements Serializable {
//    protected int rowNum;
    public static final int JSON_FIELD_TYPE_LOWEER = 1;
    public static final int JSON_FIELD_TYPE_UPPER = 2;
    
    protected String tableName;
    protected Connector connector;
    protected boolean outterConn = false;
    protected ArrayList<String> fieldNames = new ArrayList<String>();
    protected ArrayList<String> primarys = new ArrayList<String>();
    protected HashMap<String, EntityField> fields = new HashMap<String, EntityField>();

    public Entity() {
        if (this.getClass() == Entity.class) {
            return;
        }
        this.tableName = EntityCaches.getEntityTable(this.getClass().getName());
        if (FuncString.isBlank(this.tableName)) {
            Table table = this.getClass().getAnnotation(Table.class);
            if (table != null) {
                this.tableName = table.value();
            }
            Method[] methods = this.getClass().getDeclaredMethods();
            ArrayList<Column> colList =  new ArrayList<Column>();
            for (Method method : methods) {
                Column column = method.getAnnotation(Column.class);
                if (column == null) {
                    continue;
                }
                colList.add(column);
                String col = column.column().toUpperCase();
                fieldNames.add(col);
                fields.put(col, new EntityField(col, null, column.type(), column.primary()));
                if (column.primary()) {
                    this.primarys.add(col);
                }
            }
            EntityCaches.putEntity(this.getClass().getName(), this.tableName, colList);
        }
        else {
            List<Column> colList = EntityCaches.getColumns(this.getClass().getName());
            for (Column column : colList) {
                String col = column.column().toUpperCase();
                fieldNames.add(col);
                fields.put(col, new EntityField(col, null, column.type(), column.primary()));
                if (column.primary()) {
                    this.primarys.add(col);
                }
            }
        }
        
        
    }

//    public int getRowNum() {
//        return rowNum;
//    }
//
//    public Entity setRowNum(int rowNum) {
//        this.rowNum = rowNum;
//        return this;
//    }

    public Object get(String column) {
        EntityField field = fields.get(column.toUpperCase());
        if (field == null) {
            return null;
        }
        return field.getValue();
    }
    
    public Object get(int i) {
        EntityField field = this.fields.get(this.fieldNames.get(i));
        if (field == null) {
            return null;
        }
        return field.getValue();
    }
    
    public Entity set(String column, Object value) {
        EntityField field = fields.get(column.toUpperCase());
        if (field == null) {
            field = new EntityField(column, null, Types.INTEGER, false);
            fields.put(column.toUpperCase(), field);
            fieldNames.add(column.toUpperCase());
        }
        field.setValue(value);
        return this;
    }

    public boolean isPrimary(String column) {
        EntityField field = fields.get(column.toUpperCase());
        if (field == null) {
            return false;
        }
        return field.isPrimary();
    }
    
//    public Entity setPrimary(String column) {
//        if (this.isPrimary(column)) {
//            return this;
//        }
//        EntityField field = this.getField(column);
//        if (field == null) {
//            this.set(column, null);
//            field = this.getField(column);
//        }
//        field.setPrimary(true);
//        this.primarys.add(column.toUpperCase());
//        return this;
//    }

    public EntityField getField(String column) {
        return this.fields.get(column.toUpperCase());
    }

    public List<String> getPrimarys() {
        return this.primarys;
    }

    public List<String> getFieldNames() {
        return fieldNames;
    }

    public String getTableName() {
        return tableName;
    }

//    public Entity setTableName(String tableName) {
//        this.tableName = tableName;
//        return this;
//    }
    
    public Entity setConnector(Connector conn) {
        this.connector = conn;
        this.outterConn = true;
        return this;
    }
    
    private DBSession getSession() {
        DBSession session;
        if (this.outterConn) {
            session = DBSessionFactory.getSession(this.connector);
        }
        else {
            session = DBSessionFactory.getSession();
        }
        return session;
    }

    public void save() {
        DBSession session = getSession();
        session.save(this);
        session.commit();
    }
    
    public boolean isExist(){
        if(this.unique() != null){
            return true;
        }
        return false;
    }

    public void saveOrUpdate() {
        DBSession session = getSession();
        session.saveOrUpdate(this);
        session.commit();
    }
    
    public void saveIfNotExists() {
        DBSession session = getSession();
        session.saveIfNotExists(this);
        session.commit();
    }

    public void update() {
        DBSession session = getSession();
        session.update(this);
        session.commit();
    }

    public void delete() {
        DBSession session = getSession();
        session.delete(this);
        session.commit();
    }

    public List<Entity> listQuery() {
        DBSession session = getSession();
        List<Entity> result = new ArrayList<Entity>();
        Iterator<Entity> iter = session.query(this).queryIterator();
        while (iter.hasNext()) {
            result.add((Entity) iter.next());
        }
        DBSessionFactory.closeSession(session);
        return result;
    }

    public Entity unique() {
        DBSession session = getSession();
        Entity ret = (Entity) session.query(this).uniqueResult();
        DBSessionFactory.closeSession(session);
        return ret;
    }
    
    public Entity setEntity(Entity entity) {
        for (String fieldName : this.fieldNames) {
            this.getField(fieldName).setValue(entity.get(fieldName));
        }
        return this;
    }

    public Entity clone() {
        try {
            Entity entity = this.getClass().newInstance();
            entity.tableName = this.tableName;
            entity.fieldNames = (ArrayList<String>) this.fieldNames.clone();
            entity.primarys = (ArrayList<String>) this.primarys.clone();
            entity.fields = (HashMap<String, EntityField>) this.fields.clone();
            return entity;
        }
        catch (Exception e) {
        }
        return null;
    }
    
    public String getString(int i) {
        String fieldName = this.fieldNames.get(i);
        return this.getString(fieldName);
    }
    
    public String getString(String column) {
        Object o = this.get(column);
        if (o == null) {
            return null;
        }
        if (o instanceof Date) {
            EntityField field = this.getField(column);
            if (Types.TIMESTAMP == field.getJdbcType()) {
                return FuncDate.parseDateTime((Date) o);
            }
            return FuncDate.parse((Date) o);
        }
        if (o instanceof String) {
            return FuncString.encodeIfString(o).toString();
        }
        if (o instanceof InputStream) {
            return "";
        }
        return String.valueOf(o);
    }
    
    public String toString() {
        StringBuilder ret = new StringBuilder();
        ret.append(super.toString()).append(" {\n");
        for (String field : this.fieldNames) {
            ret.append("  ");
            EntityField ef = this.fields.get(field);
            if (ef.isPrimary()) {
                ret.append("*");
            }
            ret.append(ef.getName()).append(" : ").append(ef.getValue()).append("\n");
        }
        ret.append("}");
        return ret.toString();
    }
    
    public String toJSON() {
        return toJSON(JSON_FIELD_TYPE_UPPER);
    }
    
    public String toJSON(int jsonFieldType) {
        StringBuilder ret = new StringBuilder();
        ret.append("{");
//        ret.append("\"ROWNUM_\":").append(this.rowNum).append(",");
        for (String field : this.fieldNames) {
            String rf = field;
            if(jsonFieldType == JSON_FIELD_TYPE_LOWEER) {
                rf = field.toLowerCase();
            }
            ret.append("\"").append(rf).append("\":\"").append(FuncString.null2Blank(this.getString(field))).append("\",");
        }
        ret.delete(ret.length() - 1, ret.length());
        ret.append("}");
        return ret.toString();
    }
    
    public static String toJSON(Collection<Entity> entitys, String name) {
        return toJSON(JSON_FIELD_TYPE_UPPER, entitys, name, entitys.size());
    }
    
    public static String toJSON(int fieldType, Collection<Entity> entitys, String name) {
        return toJSON(fieldType, entitys, name, entitys.size());
    }
    
    public static String toJSON(Collection<Entity> entitys, String name, int totalCount) {
        return toJSON(JSON_FIELD_TYPE_UPPER, entitys, name, totalCount);
    }
    
    public static String toJSON(int jsonFieldType, Collection<Entity> entitys, String name, int totalCount) {
        StringBuilder ret = new StringBuilder();
        ret.append("{");
        ret.append("totalCount:").append(totalCount).append(",");
        ret.append(name).append(":[");
        for (Entity entity : entitys) {
            ret.append(entity.toJSON(jsonFieldType)).append(",");
        }
        if (entitys.size() > 0) {
            ret.delete(ret.length() - 1, ret.length());
        }
        ret.append("]}");
        return ret.toString();
    }
}
